// Squall Sound Manager
// (c) jimon game studio

#include "CSoundManager_Squall.h"
#include "CSound_Squall.h"
#include "CSoundGroup_Squall.h"
#include "IVariables.h"

#ifdef JE_SOUND_SQUALL

#include "squall.h"

namespace je
{
	namespace sound
	{
		IMPLEMENT_SINGLETON_N(SoundManager,CSoundManager_Squall,JE_ENGINE_FUNCTION_CALL)

		//! Squall Deamon
		class CSound_SquallDeamon:public je::jeUnknown
		{
		private:
			class SquallId
			{
			public:
				//! Buffer that releated to name
				core::IBuffer * Buffer;

				//! fake file name
				jeStringc Name;

				//! Handle
				u32 Handle;

				//! Constructor
				SquallId(core::IBuffer * Buff)
					:Buffer(Buff),Handle(0)
				{
					Buffer->Grab();
				}

				//! Destructor
				~SquallId()
				{
					Buffer->Drop();
				}
			};

			//! Squall Files Typedef
			typedef jeList<SquallId*> SquallFilesList;

			//! Squall Files
			static SquallFilesList SquallFiles;

			//! Handler Counter
			static u32 HandlerCounter;

			//! Return Handle by name
			static u32 GetHandleByName(const jeStringc & Name);

			//! Return Buffer by name
			static core::IBuffer * GetBufferByName(const jeStringc & Name);

			//! Return Buffer by handle
			static core::IBuffer * GetBufferByHandle(u32 Handle);

			//! On Close
			static void OnClose(u32 Handle);
		public:
			//! Constructor
			CSound_SquallDeamon();

			//! Destructor
			~CSound_SquallDeamon();

			//! On Load
			jeStringc OnLoad(core::IBuffer * Buffer);

			//! Squall Open File
			static u32 Squall_Open(const char * Name);

			//! Squall Read
			static s32 Squall_Read(u32 Handle,void* ReadBuffer,s32 Size);

			//! Squall Seek
			static s32 Squall_Seek(u32 Handle,s32 Position,s32 Mode);

			//! Squall Close
			static void Squall_Close(u32 Handle);
		};

		//! Squall Deamon
		CSound_SquallDeamon * SquallDeamon = NULL;

		//! Squall Files
		CSound_SquallDeamon::SquallFilesList CSound_SquallDeamon::SquallFiles;

		//! Handler Counter
		u32 CSound_SquallDeamon::HandlerCounter;

		//! Return SquallId handle by name
		u32 CSound_SquallDeamon::GetHandleByName(const jeStringc & Name)
		{
			for(SquallFilesList::Iterator Iter = SquallFiles.begin();Iter != SquallFiles.end();Iter++)
				if((*Iter)->Name == Name)
					return (*Iter)->Handle;
			return 0;
		}

		//! Return Buffer by name
		core::IBuffer * CSound_SquallDeamon::GetBufferByName(const jeStringc & Name)
		{
			for(SquallFilesList::Iterator Iter = SquallFiles.begin();Iter != SquallFiles.end();Iter++)
				if((*Iter)->Name == Name)
					return (*Iter)->Buffer;
			return NULL;
		}

		//! Return Buffer by handle
		core::IBuffer * CSound_SquallDeamon::GetBufferByHandle(u32 Handle)
		{
			for(SquallFilesList::Iterator Iter = SquallFiles.begin();Iter != SquallFiles.end();Iter++)
				if((*Iter)->Handle == Handle)
					return (*Iter)->Buffer;
			return NULL;
		}

		//! On Close
		void CSound_SquallDeamon::OnClose(u32 Handle)
		{
			for(SquallFilesList::Iterator Iter = SquallFiles.begin();Iter != SquallFiles.end();Iter++)
				if((*Iter)->Handle == Handle)
				{
					JEDELETE(*Iter)
					SquallFiles.erase(Iter);
					return;
				}
		}

		//! Constructor
		CSound_SquallDeamon::CSound_SquallDeamon()
		{
			HandlerCounter = 1;
		}

		//! Destructor
		CSound_SquallDeamon::~CSound_SquallDeamon()
		{
			for(SquallFilesList::Iterator Iter = SquallFiles.begin();Iter != SquallFiles.end();Iter++)
				JEDELETE(*Iter)

			SquallFiles.clear();
		}

		//! On Load
		jeStringc CSound_SquallDeamon::OnLoad(core::IBuffer * Buffer)
		{
			SquallId * Id = NULL;
			JENEW(Id,SquallId(Buffer))

			Id->Handle = HandlerCounter;
			Id->Name = jeStringc(HandlerCounter) + ".mp3";

			SquallFiles.push_back(Id);

			HandlerCounter++;

			return Id->Name;
		}

		//! Squall Open
		u32 CSound_SquallDeamon::Squall_Open(const char * Name)
		{
			u32 Handle = GetHandleByName(Name);

			if(Handle > 0)
			{
				core::IBuffer * Buffer = GetBufferByHandle(Handle);
				Buffer->Grab();
				Buffer->Lock(core::BLT_READ);
				Buffer->SetStreamWorkMode(true);
				Buffer->SetPosition(0);
				return Handle;
			}
			else
				return 0;
		}

		//! Squall Read
		s32 CSound_SquallDeamon::Squall_Read(u32 Handle,void* ReadBuffer,s32 Size)
		{
			core::IBuffer * Buffer = GetBufferByHandle(Handle);
			if(Buffer)
			{
				Buffer->Read(ReadBuffer,Size);
				return Size;
			}
			else
				return -1;
		}

		//! Squall Seek
		s32 CSound_SquallDeamon::Squall_Seek(u32 Handle,s32 Position,s32 Mode)
		{
			core::IBuffer * Buffer = GetBufferByHandle(Handle);
			if(Buffer)
			{
				switch(Mode)
				{
				case SEEK_CUR:
					Buffer->SetPosition(Position,true);
					break;
				case SEEK_END:
					Buffer->SetPosition(Buffer->GetSize()-1);
					break;
				case SEEK_SET:
					Buffer->SetPosition(Position);
					break;
				}

				s32 pos = (s32)Buffer->GetPosition();
				return pos;
			}
			else
				return -1;
		}

		//! Squall Close
		void CSound_SquallDeamon::Squall_Close(u32 Handle)
		{
			core::IBuffer * Buffer = GetBufferByHandle(Handle);
			if(Buffer)
			{
				Buffer->UnLock();
				Buffer->Drop();
				OnClose(Handle);
			}
		}

		//! Load Sound
		s32 CSoundManager_Squall::LoadSound(core::IBuffer * Buffer,u1 LoadToMemory)
		{
			jeStringc FileName = SquallDeamon->OnLoad(Buffer);
			s32 Sound = SQUALL_Sample_LoadFile((char*)FileName.c_str(),LoadToMemory,0);

			if(Sound < 0)
				JELOG_E(jeStringc("Cant load squall sound : ")+jeStringc(Sound))

			return Sound;
		}

		//! Constructor
		CSoundManager_Squall::CSoundManager_Squall()
		{
			if(!SquallDeamon)
				JENEW(SquallDeamon,CSound_SquallDeamon)
			else
				SquallDeamon->Grab();

			SQUALL_SetFileCallbacks(CSound_SquallDeamon::Squall_Open, CSound_SquallDeamon::Squall_Seek, CSound_SquallDeamon::Squall_Read, CSound_SquallDeamon::Squall_Close);
			SQUALL_SetMemoryCallbacks(core::Alloc, core::Free);

			if(SQUALL_Init(0) < 0)
				JELOG_E("Cant init squall lib")

			RootGroup = CreateSoundGroup();

			SetSoundManager(this);
		}

		//! Destructor
		CSoundManager_Squall::~CSoundManager_Squall()
		{
			RootGroup->Drop();

			if(core::GetVariables()->GetVariablec8("Squall_FreeAllOnExit"))
				SQUALL_Free();

			if(SquallDeamon->Drop())
				SquallDeamon = NULL;

			SetSoundManager(NULL);
		}

		//! Create Sound
		ISound * CSoundManager_Squall::CreateSound(core::IBuffer * Buffer,ISoundGroup * Group,u1 LoadToMemory)
		{
			CSound_Squall * Sound = NULL;
			JENEW(Sound,CSound_Squall(LoadSound(Buffer,LoadToMemory)))

			RootGroup->AddSound(Sound);
			Sound->Drop();

			if(Group)
				Group->AddSound(Sound);

			return Sound;
		}

		//! Create Sound Group
		ISoundGroup * CSoundManager_Squall::CreateSoundGroup()
		{
			CSoundGroup_Squall * SoundGroup = NULL;
			JENEW(SoundGroup,CSoundGroup_Squall)
			return SoundGroup;
		}

		//! Get Root Group
		ISoundGroup * CSoundManager_Squall::GetRootGroup()
		{
			return RootGroup;
		}
	}
}

#endif
